<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>flask 源码解析：路由 | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="flask 源码解析：路由 | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2017/01/12/flask-insight-routing/">

	
	<meta property="article:published_time" content="2017-01-12T00:01:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2017/01/12/flask-insight-routing/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2017-01-11T16:00:00.000Z" itemprop="datePublished">
                    2017-01-12
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/blog/">blog</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">flask 源码解析：路由</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <p>这是 flask 源码解析系列文章的其中一篇，本系列所有文章列表：</p>
<ul>
<li><a href="http://cizixs.com/2017/01/10/flask-insight-introduction">flask 源码解析：简介</a></li>
<li><a href="http://cizixs.com/2017/01/11/flask-insight-start-process">flask 源码解析：应用启动流程</a></li>
<li><a href="http://cizixs.com/2017/01/12/flask-insight-routing">flask 源码解析：路由</a></li>
<li><a href="http://cizixs.com/2017/01/13/flask-insight-context">flask 源码解析：上下文</a></li>
<li><a href="http://cizixs.com/2017/01/18/flask-insight-request">flask 源码解析：请求</a></li>
<li><a href="http://cizixs.com/2017/01/22/flask-insight-response">flask 源码解析：响应</a></li>
<li><a href="http://cizixs.com/2017/03/08/flask-insight-session">flask 源码解析：session</a></li>
</ul>
<h2 id="构建路由规则"><a href="#构建路由规则" class="headerlink" title="构建路由规则"></a>构建路由规则</h2><p>一个 web 应用不同的路径会有不同的处理函数，<strong>路由就是根据请求的 URL 找到对应处理函数的过程。</strong></p>
<p>在执行查找之前，需要有一个规则列表，它存储了 url 和处理函数的对应关系。最容易想到的解决方案就是定义一个字典，key 是 url，value 是对应的处理函数。如果 url 都是静态的（url 路径都是实现确定的，没有变量和正则匹配），那么路由的过程就是从字典中通过 url 这个 key ，找到并返回对应的 value；如果没有找到，就报 404 错误。而对于动态路由，还需要更复杂的匹配逻辑。flask 中的路由过程是这样的吗？这篇文章就来分析分析。</p>
<p>在分析路由匹配过程之前，我们先来看看 <code>flask</code> 中，<a href="http://flask.pocoo.org/docs/0.12/api/#url-route-registrations" target="_blank" rel="noopener">构建这个路由规则</a>的两种方法：</p>
<ol>
<li>通过 <a href="mailto:`@app.route" target="_blank" rel="noopener">`@app.route</a>()`  decorator，比如文章开头给出的 hello world 例子</li>
<li>通过 <code>app.add_url_rule</code>，这个方法的签名为 <code>add_url_rule(self, rule, endpoint=None, view_func=None, **options)</code>，参数的含义如下：<ul>
<li><code>rule</code>： url 规则字符串，可以是静态的 <code>/path</code>，也可以包含 <code>/</code></li>
<li><code>endpoint</code>：要注册规则的 endpoint，默认是 <code>view_func</code> 的名字</li>
<li><code>view_func</code>：对应 url 的处理函数，也被称为视图函数</li>
</ul>
</li>
</ol>
<p>这两种方法是等价的，也就是说：</p>
<pre class=" language-python"><code class="language-python">@app<span class="token punctuation">.</span>route<span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token string">"hello, world!"</span>
</code></pre>
<p>也可以写成</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> <span class="token string">"hello, world!"</span>

app<span class="token punctuation">.</span>add_url_rule<span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token string">'hello'</span><span class="token punctuation">,</span> hello<span class="token punctuation">)</span>
</code></pre>
<p><strong>NOTE</strong>: 其实，还有一种方法来构建路由规则——直接操作 <code>app.url_map</code> 这个数据结构。不过这种方法并不是很常用，因此就不展开了。</p>
<p>注册路由规则的时候，flask 内部做了哪些东西呢？我们来看看 <code>route</code> 方法：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">route</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> rule<span class="token punctuation">,</span> <span class="token operator">**</span>options<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""A decorator that is used to register a view function for a
    given URL rule.  This does the same thing as :meth:`add_url_rule`
    but is intended for decorator usage.
    """</span>

    <span class="token keyword">def</span> <span class="token function">decorator</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span><span class="token punctuation">:</span>
        endpoint <span class="token operator">=</span> options<span class="token punctuation">.</span>pop<span class="token punctuation">(</span><span class="token string">'endpoint'</span><span class="token punctuation">,</span> None<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>add_url_rule<span class="token punctuation">(</span>rule<span class="token punctuation">,</span> endpoint<span class="token punctuation">,</span> f<span class="token punctuation">,</span> <span class="token operator">**</span>options<span class="token punctuation">)</span>
        <span class="token keyword">return</span> f

    <span class="token keyword">return</span> decorator
</code></pre>
<p><code>route</code> 方法内部也是调用 <code>add_url_rule</code>，只不过在外面包了一层装饰器的逻辑，这也验证了上面两种方法等价的说法。</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">add_url_rule</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> rule<span class="token punctuation">,</span> endpoint<span class="token operator">=</span>None<span class="token punctuation">,</span> view_func<span class="token operator">=</span>None<span class="token punctuation">,</span> <span class="token operator">**</span>options<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Connects a URL rule.  Works exactly like the :meth:`route`
    decorator.  If a view_func is provided it will be registered with the
    endpoint.
    """</span>

    methods <span class="token operator">=</span> options<span class="token punctuation">.</span>pop<span class="token punctuation">(</span><span class="token string">'methods'</span><span class="token punctuation">,</span> None<span class="token punctuation">)</span>

    rule <span class="token operator">=</span> self<span class="token punctuation">.</span>url_rule_class<span class="token punctuation">(</span>rule<span class="token punctuation">,</span> methods<span class="token operator">=</span>methods<span class="token punctuation">,</span> <span class="token operator">**</span>options<span class="token punctuation">)</span>
    self<span class="token punctuation">.</span>url_map<span class="token punctuation">.</span>add<span class="token punctuation">(</span>rule<span class="token punctuation">)</span>
    <span class="token keyword">if</span> view_func <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
        old_func <span class="token operator">=</span> self<span class="token punctuation">.</span>view_functions<span class="token punctuation">.</span>get<span class="token punctuation">(</span>endpoint<span class="token punctuation">)</span>
        <span class="token keyword">if</span> old_func <span class="token keyword">is</span> <span class="token operator">not</span> None <span class="token operator">and</span> old_func <span class="token operator">!=</span> view_func<span class="token punctuation">:</span>
            <span class="token keyword">raise</span> AssertionError<span class="token punctuation">(</span><span class="token string">'View function mapping is overwriting an '</span>
                                 <span class="token string">'existing endpoint function: %s'</span> <span class="token operator">%</span> endpoint<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>view_functions<span class="token punctuation">[</span>endpoint<span class="token punctuation">]</span> <span class="token operator">=</span> view_func
</code></pre>
<p>上面这段代码省略了处理 endpoint 和构建 methods 的部分逻辑，可以看到它主要做的事情就是更新 <code>self.url_map</code> 和 <code>self.view_functions</code> 两个变量。找到变量的定义，发现 <code>url_map</code>  是 <code>werkzeug.routeing:Map</code> 类的对象，<code>rule</code> 是 <code>werkzeug.routing:Rule</code> 类的对象，<code>view_functions</code> 就是一个字典。这和我们之前预想的并不一样，这里增加了 <code>Rule</code> 和 <code>Map</code> 的封装，还把 <code>url</code> 和 <code>view_func</code> 保存到了不同的地方。</p>
<p>需要注意的是：每个视图函数的 endpoint 必须是不同的，否则会报 <code>AssertionError</code>。</p>
<h2 id="werkzeug-路由逻辑"><a href="#werkzeug-路由逻辑" class="headerlink" title="werkzeug 路由逻辑"></a>werkzeug 路由逻辑</h2><p>事实上，flask 核心的路由逻辑是在 <code>werkzeug</code> 中实现的。所以在继续分析之前，我们先看一下 <code>werkzeug</code> 提供的<a href="http://werkzeug.pocoo.org/docs/0.11/routing/" target="_blank" rel="noopener">路由功能</a>。</p>
<pre class=" language-python"><code class="language-python"><span class="token operator">>></span><span class="token operator">></span> m <span class="token operator">=</span> Map<span class="token punctuation">(</span><span class="token punctuation">[</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>     Rule<span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> endpoint<span class="token operator">=</span><span class="token string">'index'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>     Rule<span class="token punctuation">(</span><span class="token string">'/downloads/'</span><span class="token punctuation">,</span> endpoint<span class="token operator">=</span><span class="token string">'downloads/index'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>     Rule<span class="token punctuation">(</span><span class="token string">'/downloads/&lt;int:id>'</span><span class="token punctuation">,</span> endpoint<span class="token operator">=</span><span class="token string">'downloads/show'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token operator">>></span><span class="token operator">></span> urls <span class="token operator">=</span> m<span class="token punctuation">.</span>bind<span class="token punctuation">(</span><span class="token string">"example.com"</span><span class="token punctuation">,</span> <span class="token string">"/"</span><span class="token punctuation">)</span>
<span class="token operator">>></span><span class="token operator">></span> urls<span class="token punctuation">.</span>match<span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">,</span> <span class="token string">"GET"</span><span class="token punctuation">)</span>
<span class="token punctuation">(</span><span class="token string">'index'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator">>></span><span class="token operator">></span> urls<span class="token punctuation">.</span>match<span class="token punctuation">(</span><span class="token string">"/downloads/42"</span><span class="token punctuation">)</span>
<span class="token punctuation">(</span><span class="token string">'downloads/show'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token string">'id'</span><span class="token punctuation">:</span> <span class="token number">42</span><span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token operator">>></span><span class="token operator">></span> urls<span class="token punctuation">.</span>match<span class="token punctuation">(</span><span class="token string">"/downloads"</span><span class="token punctuation">)</span>
Traceback <span class="token punctuation">(</span>most recent call last<span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
RequestRedirect<span class="token punctuation">:</span> http<span class="token punctuation">:</span><span class="token operator">//</span>example<span class="token punctuation">.</span>com<span class="token operator">/</span>downloads<span class="token operator">/</span>
<span class="token operator">>></span><span class="token operator">></span> urls<span class="token punctuation">.</span>match<span class="token punctuation">(</span><span class="token string">"/missing"</span><span class="token punctuation">)</span>
Traceback <span class="token punctuation">(</span>most recent call last<span class="token punctuation">)</span><span class="token punctuation">:</span>
  <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
NotFound<span class="token punctuation">:</span> <span class="token number">404</span> Not Found
</code></pre>
<p>上面的代码演示了 <code>werkzeug</code> 最核心的路由功能：添加路由规则（也可以使用 <code>m.add</code>），把路由表绑定到特定的环境（<code>m.bind</code>），匹配url（<code>urls.match</code>）。正常情况下返回对应的 endpoint 名字和参数字典，可能报重定向或者 404 异常。</p>
<p>可以发现，<a href="http://stackoverflow.com/a/19262349/1925083" target="_blank" rel="noopener"><code>endpoint</code> 在路由过程中非常重要</a>。<code>werkzeug</code> 的路由过程，其实是 url 到 endpoint 的转换：通过 url 找到处理该 url 的 endpoint。至于 endpoint 和 view function 之间的匹配关系，<code>werkzeug</code> 是不管的，而上面也看到 <code>flask</code> 是把这个存放到字典中的。</p>
<h2 id="flask-路由实现"><a href="#flask-路由实现" class="headerlink" title="flask 路由实现"></a>flask 路由实现</h2><p>好，有了这些基础知识，我们回头看 <code>dispatch_request</code>，继续探寻路由匹配的逻辑：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">dispatch_request</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Does the request dispatching.  Matches the URL and returns the
    return value of the view or error handler.  This does not have to
    be a response object.  In order to convert the return value to a
    proper response object, call :func:`make_response`.
    """</span>

    req <span class="token operator">=</span> _request_ctx_stack<span class="token punctuation">.</span>top<span class="token punctuation">.</span>request
    <span class="token keyword">if</span> req<span class="token punctuation">.</span>routing_exception <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>raise_routing_exception<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
    rule <span class="token operator">=</span> req<span class="token punctuation">.</span>url_rule

    <span class="token comment" spellcheck="true"># dispatch to the handler for that endpoint</span>
    <span class="token keyword">return</span> self<span class="token punctuation">.</span>view_functions<span class="token punctuation">[</span>rule<span class="token punctuation">.</span>endpoint<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token operator">**</span>req<span class="token punctuation">.</span>view_args<span class="token punctuation">)</span>
</code></pre>
<p>这个方法做的事情就是找到请求对象 <code>request</code>，获取它的 <code>endpoint</code>，然后从 <code>view_functions</code> 找到对应 <code>endpoint</code> 的 <code>view_func</code> ，把请求参数传递过去，进行处理并返回。<code>view_functions</code> 中的内容，我们已经看到，是在构建路由规则的时候保存进去的；那请求中 <code>req.url_rule</code> 是什么保存进去的呢？它的格式又是什么？</p>
<p>我们可以先这样理解：<code>_request_ctx_stack.top.request</code> 保存着当前请求的信息，在每次请求过来的时候，<code>flask</code> 会把当前请求的信息保存进去，这样我们就能在整个请求处理过程中使用它。至于怎么做到并发情况下信息不会相互干扰错乱，我们将在下一篇文章介绍。</p>
<p><code>_request_ctx_stack</code> 中保存的是 <code>RequestContext</code> 对象，它出现在 <code>flask/ctx.py</code> 文件中，和路由相关的逻辑如下：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">RequestContext</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> app<span class="token punctuation">,</span> environ<span class="token punctuation">,</span> request<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>app <span class="token operator">=</span> app
        self<span class="token punctuation">.</span>request <span class="token operator">=</span> request
        self<span class="token punctuation">.</span>url_adapter <span class="token operator">=</span> app<span class="token punctuation">.</span>create_url_adapter<span class="token punctuation">(</span>self<span class="token punctuation">.</span>request<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>match_request<span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">match_request</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Can be overridden by a subclass to hook into the matching
        of the request.
        """</span>
        <span class="token keyword">try</span><span class="token punctuation">:</span>
            url_rule<span class="token punctuation">,</span> self<span class="token punctuation">.</span>request<span class="token punctuation">.</span>view_args <span class="token operator">=</span> \
                self<span class="token punctuation">.</span>url_adapter<span class="token punctuation">.</span>match<span class="token punctuation">(</span>return_rule<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
            self<span class="token punctuation">.</span>request<span class="token punctuation">.</span>url_rule <span class="token operator">=</span> url_rule
        <span class="token keyword">except</span> HTTPException <span class="token keyword">as</span> e<span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>request<span class="token punctuation">.</span>routing_exception <span class="token operator">=</span> e


<span class="token keyword">class</span> <span class="token class-name">Flask</span><span class="token punctuation">(</span>_PackageBoundObject<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">create_url_adapter</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> request<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Creates a URL adapter for the given request.  The URL adapter
        is created at a point where the request context is not yet set up
        so the request is passed explicitly.
        """</span>
        <span class="token keyword">if</span> request <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">return</span> self<span class="token punctuation">.</span>url_map<span class="token punctuation">.</span>bind_to_environ<span class="token punctuation">(</span>request<span class="token punctuation">.</span>environ<span class="token punctuation">,</span>
                server_name<span class="token operator">=</span>self<span class="token punctuation">.</span>config<span class="token punctuation">[</span><span class="token string">'SERVER_NAME'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</code></pre>
<p>在初始化的时候，会调用 <code>app.create_url_adapter</code> 方法，把 <code>app</code> 的 <code>url_map</code> 绑定到 WSGI environ 变量上（<code>bind_to_environ</code> 和之前的 <code>bind</code> 方法作用相同）。最后会调用 <code>match_request</code> 方法，这个方式调用了 <code>url_adapter.match</code> 方法，进行实际的匹配工作，返回匹配的 url rule。而我们之前使用的 <code>url_rule.endpoint</code> 就是匹配的 endpoint 值。</p>
<p>整个 <code>flask</code> 的路由过程就结束了，总结一下大致的流程：</p>
<ul>
<li>通过 <a href="mailto:`@app.route" target="_blank" rel="noopener">`@app.route</a><code>或者</code>app.add_url_rule` 注册应用 url 对应的处理函数</li>
<li>每次请求过来的时候，会事先调用路由匹配的逻辑，把路由结果保存起来</li>
<li><code>dispatch_request</code> 根据保存的路由结果，调用对应的视图函数</li>
</ul>
<h2 id="match-实现"><a href="#match-实现" class="headerlink" title="match 实现"></a>match 实现</h2><p>虽然讲完了 <code>flask</code> 的路由流程，但是还没有讲到最核心的问题：<code>werkzeug</code> 中是怎么实现  <code>match</code> 方法的。<code>Map</code> 保存了 <code>Rule</code> 列表，<code>match</code> 的时候会依次调用其中的 <code>rule.match</code> 方法，如果匹配就找到了 match。<code>Rule.match</code> 方法的代码如下：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">match</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> path<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Check if the rule matches a given path. Path is a string in the
        form ``"subdomain|/path(method)"`` and is assembled by the map.  If
        the map is doing host matching the subdomain part will be the host
        instead.

        If the rule matches a dict with the converted values is returned,
        otherwise the return value is `None`.
        """</span>
        <span class="token keyword">if</span> <span class="token operator">not</span> self<span class="token punctuation">.</span>build_only<span class="token punctuation">:</span>
            m <span class="token operator">=</span> self<span class="token punctuation">.</span>_regex<span class="token punctuation">.</span>search<span class="token punctuation">(</span>path<span class="token punctuation">)</span>
            <span class="token keyword">if</span> m <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
                groups <span class="token operator">=</span> m<span class="token punctuation">.</span>groupdict<span class="token punctuation">(</span><span class="token punctuation">)</span>

                result <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
                <span class="token keyword">for</span> name<span class="token punctuation">,</span> value <span class="token keyword">in</span> iteritems<span class="token punctuation">(</span>groups<span class="token punctuation">)</span><span class="token punctuation">:</span>
                    <span class="token keyword">try</span><span class="token punctuation">:</span>
                        value <span class="token operator">=</span> self<span class="token punctuation">.</span>_converters<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">.</span>to_python<span class="token punctuation">(</span>value<span class="token punctuation">)</span>
                    <span class="token keyword">except</span> ValidationError<span class="token punctuation">:</span>
                        <span class="token keyword">return</span>
                    result<span class="token punctuation">[</span>str<span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token operator">=</span> value
                <span class="token keyword">if</span> self<span class="token punctuation">.</span>defaults<span class="token punctuation">:</span>
                    result<span class="token punctuation">.</span>update<span class="token punctuation">(</span>self<span class="token punctuation">.</span>defaults<span class="token punctuation">)</span>

                <span class="token keyword">return</span> result
</code></pre>
<p>它的逻辑是这样的：用实现 compile 的正则表达式去匹配给出的真实路径信息，把所有的匹配组件转换成对应的值，保存在字典中（这就是传递给视图函数的参数列表）并返回。</p>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="flask 源码解析：上下文" href="/2017/01/13/flask-insight-context/">
        ← flask 源码解析：上下文
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="flask 源码解析：应用启动流程" href="/2017/01/11/flask-insight-start-process/">
        flask 源码解析：应用启动流程 →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#构建路由规则"><span class="toc-text">构建路由规则</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#werkzeug-路由逻辑"><span class="toc-text">werkzeug 路由逻辑</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#flask-路由实现"><span class="toc-text">flask 路由实现</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#match-实现"><span class="toc-text">match 实现</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">flask 源码解析：路由</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2017/01/12/flask-insight-routing/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2017/01/12/flask-insight-routing/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
